/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "sysvpn_utils.h"

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <utils/debug.h>
#include "cm_request.h"
#include "cm_mem.h"
#include "cm_param.h"
#include "cm_ipc_client_serialization.h"

int32_t InitCertList(struct CertList **cList)
{
    *cList = (struct CertList *)(malloc(sizeof(struct CertList)));
    if (*cList == NULL) {
        DBG2(DBG_CFG, "rzc InitCertList failed");
        return CMR_ERROR_MALLOC_FAIL;
    }

    uint32_t buffSize = MAX_COUNT_CERTIFICATE * sizeof(struct CertAbstract);
    (*cList)->certAbstract = (struct CertAbstract *)(malloc(buffSize));
    if ((*cList)->certAbstract == NULL) {
        DBG2(DBG_CFG, "rzc InitCertList certAbstract failed");
        return CMR_ERROR_MALLOC_FAIL;
    }
    (void)memset_s((*cList)->certAbstract, buffSize, 0, buffSize);
    (*cList)->certsCount = MAX_COUNT_CERTIFICATE;

    return CM_SUCCESS;
}

int32_t CmSendParcelInit(struct CmParam *params, uint32_t paramCount,
    struct CmBlob *parcelBlob, struct CmParamSet **sendParamSet)
{
    int32_t ret = CM_SUCCESS;

    ret = CmParamsToParamSet(params, paramCount, sendParamSet);
    if (ret != CM_SUCCESS) {
        DBG2(DBG_CFG, "rzc CmParamSetPack fail");
        return ret;
    }

    parcelBlob->size = (*sendParamSet)->paramSetSize;
    parcelBlob->data = (uint8_t *)*sendParamSet;
    return ret;
}

int32_t GetCertListInitOutData(struct CmBlob *outListBlob)
{
    /* buff struct: certCount + MAX_CERT_COUNT * (subjectBlob + status + uriBlob + aliasBlob) */
    uint32_t buffSize = sizeof(uint32_t) + (sizeof(uint32_t) + MAX_LEN_SUBJECT_NAME + sizeof(uint32_t) +
        sizeof(uint32_t) + MAX_LEN_URI + sizeof(uint32_t) +  MAX_LEN_CERT_ALIAS) * MAX_COUNT_CERTIFICATE;
    outListBlob->data = (uint8_t *)CmMalloc(buffSize);
    if (outListBlob->data == NULL) {
        return CMR_ERROR_MALLOC_FAIL;
    }
    outListBlob->size = buffSize;

    return CM_SUCCESS;
}

int32_t GetUserCertList(enum CertManagerInterfaceCode type, const uint32_t store,
    struct CertList *certificateList)
{
    int32_t ret = CM_SUCCESS;
    struct CmBlob outBlob = {0, NULL};
    struct CmBlob parcelBlob = {0, NULL};
    struct CmParamSet *sendParamSet = NULL;
    struct CmParam params[] = {
        { .tag = CM_TAG_PARAM0_UINT32, .uint32Param = store },
    };

    do {
        ret = CmSendParcelInit(params, CM_ARRAY_SIZE(params), &parcelBlob, &sendParamSet);
        if (ret != CM_SUCCESS) {
            DBG2(DBG_CFG, "rzc get cert list sendParcel failed");
            break;
        }

        ret = GetCertListInitOutData(&outBlob);
        if (ret != CM_SUCCESS) {
            DBG2(DBG_CFG, "rzc malloc getcertlist outdata failed");
            break;
        }

        ret = SendRequest(type, &parcelBlob, &outBlob);
        if (ret != CM_SUCCESS) {
            DBG2(DBG_CFG, "rzc GetCertList request failed, ret: %d", ret);
            break;
        }

        ret = CmCertificateListUnpackFromService(&outBlob, certificateList);
        if (ret != CM_SUCCESS) {
            DBG2(DBG_CFG, "rzc getcertlist unpack from service failed");
            break;
        }
    } while (0);

    CmFreeParamSet(&sendParamSet);
    CM_FREE_BLOB(outBlob);
    return ret;
}

void getCaCertList()
{
  struct CertList *certList = NULL;
  int32_t ret = InitCertList(&certList);
  if (ret != CM_SUCCESS) {
    DBG2(DBG_CFG, "rzc InitCertList failed");
  }
  ret =  GetUserCertList(CM_MSG_GET_USER_CERTIFICATE_LIST, CM_USER_TRUSTED_STORE, certList);
  if (ret == CM_SUCCESS) {
    for (size_t i = 0; i < certList->certsCount; i++)
    {
        DBG2(DBG_CFG, "rzc getAppCertList index = %d, uri = %s",i, certList->certAbstract[i].uri);
        DBG2(DBG_CFG, "rzc getAppCertList index = %d, certAlias = %s",i, certList->certAbstract[i].certAlias);
        DBG2(DBG_CFG, "rzc getAppCertList index = %d, subjectName = %s",i, certList->certAbstract[i].subjectName);
    }
  }
}

int32_t InitUserCertInfo(struct CertInfo **cInfo)
{
    *cInfo = (struct CertInfo *)(malloc(sizeof(struct CertInfo)));
    if (*cInfo == NULL) {
        return CMR_ERROR_MALLOC_FAIL;
    }
    (void)memset_s(*cInfo, sizeof(struct CertInfo), 0, sizeof(struct CertInfo));

    (*cInfo)->certInfo.data = (uint8_t *)(malloc(MAX_LEN_CERTIFICATE));
    if ((*cInfo)->certInfo.data == NULL) {
        return CMR_ERROR_MALLOC_FAIL;
    }
    (*cInfo)->certInfo.size = MAX_LEN_CERTIFICATE;

    return CM_SUCCESS;
}

int32_t GetCertInfoInitOutData(struct CmBlob *outInfoBlob)
{
    /* buff struct: certDataBlob + status + aliasBlob */
    uint32_t buffSize = sizeof(uint32_t) + MAX_LEN_CERTIFICATE + sizeof(uint32_t) +
        MAX_LEN_CERT_ALIAS + sizeof(uint32_t);

    outInfoBlob->data = (uint8_t *)CmMalloc(buffSize);
    if (outInfoBlob->data == NULL) {
        return CMR_ERROR_MALLOC_FAIL;
    }
    outInfoBlob->size = buffSize;

    return CM_SUCCESS;
}

int32_t GetUserCertInfo(enum CertManagerInterfaceCode type, const struct CmBlob *certUri,
    const uint32_t store, struct CertInfo *userCertInfo)
{
    int32_t ret = CM_SUCCESS;
    struct CmBlob outBlob = {0, NULL};
    struct CmBlob parcelBlob = {0, NULL};
    struct CmParamSet *sendParamSet = NULL;
    struct CmParam params[] = {
        { .tag = CM_TAG_PARAM0_BUFFER, .blob = *certUri },
        { .tag = CM_TAG_PARAM0_UINT32, .uint32Param = store },
    };

    do {
        ret = CmSendParcelInit(params, CM_ARRAY_SIZE(params), &parcelBlob, &sendParamSet);
        if (ret != CM_SUCCESS) {
            DBG2(DBG_CFG, "rzc get cert info sendParcel failed");
            break;
        }

        ret = GetCertInfoInitOutData(&outBlob);
        if (ret != CM_SUCCESS) {
            DBG2(DBG_CFG, "rzc malloc getcertinfo outdata failed");
            break;
        }

        ret = SendRequest(type, &parcelBlob, &outBlob);
        if (ret != CM_SUCCESS) {
            DBG2(DBG_CFG, "rzc GetCertInfo request failed, ret: %d", ret);
            break;
        }

        ret = CmCertificateInfoUnpackFromService(&outBlob, certUri, userCertInfo);
        if (ret != CM_SUCCESS) {
            DBG2(DBG_CFG, "rzc getcertinfo unpack from service failed");
            break;
        }
    } while (0);
    CmFreeParamSet(&sendParamSet);
    CM_FREE_BLOB(outBlob);
    return ret;
}

void getCacertInfo(){
  char *uriName = "oh:t=c;o=vpnca;u=100;a=20010011";
  struct CmBlob uri;
  uri.size = strlen(uriName) + 1;
  uri.data = (uint8_t *)uriName;
  struct CertInfo *certInfo = NULL;
  int32_t ret = InitUserCertInfo(&certInfo);
  if (ret != CM_SUCCESS) {
    DBG2(DBG_CFG, "rzc InitUserCertInfo failed");
  }
  ret = GetUserCertInfo(CM_MSG_GET_USER_CERTIFICATE_INFO, &uri, CM_USER_TRUSTED_STORE, certInfo);
}

void WriteStatus(char *info)
{
  if (!info) {
    return;
  }

  struct sockaddr_un address;
  int sockfd = -1;
  if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
    DBG1(DBG_LIB, "sysvpn socket fail");
    return;
  }

  address.sun_family = AF_UNIX;
  strncpy(address.sun_path, IPSECSOCKET, strlen(IPSECSOCKET));
  int nlength = sizeof(address);

  int result = connect(sockfd, (struct sockaddr *)&address, nlength);
  if (result == -1) {
    DBG1(DBG_LIB, "ensure the server is up");
    close(sockfd);
    return;
  }
  if (send(sockfd, info, strlen(info), 0) <= 0) {
    DBG1(DBG_LIB, "send data is fail");
    close(sockfd);
    return;
  }
  close(sockfd);
}

/**
 * check ipsec.conf file exist function for starter
 */
bool checkIpsecFile()
{
  bool bret = false;
  if (access(IPSECCONFFILE, F_OK) != -1) {
    bret = true;
  } else {
    bret = false;
  }
  return bret;
}